package test.myorm.chain;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ProxyFactory {
	
	private Chain chain;
	
	public ProxyFactory() {
		super();
	}

	public ProxyFactory(Chain chain) {
		this.chain = chain;
	}
	
	public Object getService(Object obj) {
		return getService(obj, chain);
	}
	
	public Object getService(Object obj, Chain chain) {
		List<Interceptor> interceptors = chain.getInterceptors();
		System.out.println(interceptors);
		
		Enhancer enhancer = new Enhancer();
	    enhancer.setSuperclass(obj.getClass());
	    
	    Map<Interceptor, Invocation> map = new HashMap<>();
	    int i = interceptors.size() - 1;
		while(true) {
			Interceptor interceptor = interceptors.get(i);
			Invocation invocation = new Invocation();
			if(i < interceptors.size() - 1) {
				Interceptor next = interceptors.get(i + 1);
				Method method = null;
				try {
					method = next.getClass().getMethod("invoke", Invocation.class);
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				}
				invocation.setMethod(method);
				invocation.setArgs(new Object[] {map.get(next)});
				invocation.setTarget(next);
			}
			map.put(interceptor, invocation);
			if(i==0) {
				break;
			}
			i--;
		}
	    enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				Interceptor last = interceptors.get(interceptors.size() - 1);
				Invocation lastInvocation = map.get(last);
				lastInvocation.setMethod(MethodProxy.class.getMethod("invokeSuper", Object.class, Object[].class));
				lastInvocation.setArgs(new Object[] {target, args});
				lastInvocation.setTarget(proxy);
				
				for(Interceptor interceptor : interceptors) {
					Invocation invocation = map.get(interceptor);
					invocation.setRealArgs(args);
				}
				
				Interceptor first = interceptors.get(0);
				Object result = first.invoke(map.get(first));
//			    return proxy.invokeSuper(target, args);
//			    return method.invoke(target, args);
				return result;
//				return null;
			}
		});
		return enhancer.create();
		
	}

}
